/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package it.polimi.meteocal.sessionbeans;

import it.polimi.meteocal.entities.Event;
import it.polimi.meteocal.sessionbeans.NotificationManager;
import java.io.IOException;
import static java.lang.Thread.sleep;
import java.util.Calendar;
import static java.util.Calendar.DAY_OF_MONTH;
import static java.util.Calendar.HOUR_OF_DAY;
import static java.util.Calendar.MILLISECOND;
import static java.util.Calendar.MINUTE;
import static java.util.Calendar.MONTH;
import static java.util.Calendar.SECOND;
import static java.util.Calendar.YEAR;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Schedule;
import javax.ejb.ScheduleExpression;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import net.aksingh.owmjapis.AbstractForecast.Forecast;
import net.aksingh.owmjapis.DailyForecast;
import net.aksingh.owmjapis.OpenWeatherMap;
import org.apache.commons.lang3.time.DateUtils;

@Startup
@Singleton
public class WeatherManager{
   
    @PersistenceContext
    EntityManager em;
    
    @EJB
    NotificationManager nm;
    
    @Resource
    private TimerService timerService;
        
    public WeatherManager(){  }
    
    @PostConstruct
    private void init() {    
        ScheduleExpression every5Seconds = new ScheduleExpression().second("*/00").minute("*").hour("*");
        timerService.createCalendarTimer(every5Seconds, new TimerConfig("", false));
    }
    
    @Timeout 
    public void checkWeatherProgTimer(Timer timer) throws IOException, InterruptedException {
            System.out.println("BAAAAAAAAAAAAAAAAAAAAAAA00000011111099");
            List<Event> outdoorEvents = em.createNamedQuery("Event.findValidOutdoor").getResultList();
            OpenWeatherMap owm = new OpenWeatherMap("9c78584b0bbe0bf460e00eeebd09a926");
            DailyForecast dailyForecastList = null;
            Forecast eventDayForecast = null;
            Date now = new Date();
            int size; 
            String weather;
            float temperature;
            
            /* Controllo di tutti gli eventi OUTDOOR */
            for(Event e : outdoorEvents){
                size = 0;
                /* Gestione di un evento a 3 giorni dalla data corrente */
                if(threeDaysBefore(now, e.getStartHour())){
                    while(size < 4){ /* Finché la richiesta al meteo da' errore (size = 0), riprova */                            
                        dailyForecastList = owm.dailyForecastByCityName(e.getCity(), Byte.parseByte("16"));  
                        size = dailyForecastList.getForecastCount();
                        System.out.println("size " + size);
                    }
                    eventDayForecast = dailyForecastList.getForecastInstance(3);
                    weather = eventDayForecast.getWeatherInstance(0).getWeatherName();
                    temperature = dailyForecastList.getForecastInstance(3).getTemperatureInstance().getDayTemperature();
                    this.persistForecast(e, weather, temperature);
                        
                    if(weather.equals("Rain") || weather.equals("Snow")){   
                        for(int i = 0; i < size; i++){
                            Forecast f = dailyForecastList.getForecastInstance(i);
                            System.out.println("citta " + e.getCity() +"date " + f.getDateTime() + " weather " + f.getWeatherInstance(0).getWeatherName() + " temp " + dailyForecastList.getForecastInstance(i).getTemperatureInstance().getDayTemperature());
                            
                        }
                        Date proposedDate = this.findClosestGoodDay(dailyForecastList, e);
                        nm.sendNewDayNotification(e, proposedDate);                               
                    }
                } 
               
                /* Gestione di un evento a 1 giorno dalla data corrente */
                else if(oneDayBefore(now, e.getStartHour())){
                    while(size < 2){
                        dailyForecastList = owm.dailyForecastByCityName(e.getCity(), Byte.parseByte("5"));  
                        size = dailyForecastList.getForecastCount();
                        System.out.println("size00 " + size);                        
                    }
                    eventDayForecast = dailyForecastList.getForecastInstance(1);
                    weather = eventDayForecast.getWeatherInstance(0).getWeatherName();
                    temperature = dailyForecastList.getForecastInstance(1).getTemperatureInstance().getDayTemperature();
                    this.persistForecast(e, weather, temperature);

                    if(weather.equals("Rain") || weather.equals("Snow")){
                        nm.sendBadWeatherNotification(e);                               
                    }
                }
            } /* end for */
    }
                    
    public boolean threeDaysBefore(Date now, Date eventStart){
        Date threeDaysFromNow = DateUtils.addDays(now, 3);
        Calendar threeDaysCal = Calendar.getInstance();
        threeDaysCal.setTime(threeDaysFromNow);
        
        Calendar eventStartCal = Calendar.getInstance();
        eventStartCal.setTime(eventStart);
        
        if(threeDaysCal.get(DAY_OF_MONTH) == eventStartCal.get(DAY_OF_MONTH) &&
           threeDaysCal.get(MONTH) == eventStartCal.get(MONTH) && 
           threeDaysCal.get(YEAR) == eventStartCal.get(YEAR)){
            return true;
        }
        else return false;      
    }
    
    public boolean oneDayBefore(Date now, Date eventStart){
        Date oneDayFromNow = DateUtils.addDays(now, 1);
        Calendar oneDayCal = Calendar.getInstance();
        oneDayCal.setTime(oneDayFromNow);
        
        Calendar eventStartCal = Calendar.getInstance();
        eventStartCal.setTime(eventStart);
        
        if(oneDayCal.get(DAY_OF_MONTH) == eventStartCal.get(DAY_OF_MONTH) &&
           oneDayCal.get(MONTH) == eventStartCal.get(MONTH) && 
           oneDayCal.get(YEAR) == eventStartCal.get(YEAR)){
            return true;
        }
        else return false;      
    }
    
    public void persistForecast(Event e, String w, float t){
        e.setWeather(w); 
        e.setTemperature(Integer.toString((int)Math.round((t-32)/1.8)));        
        em.merge(e);
    }
    
    public Date findClosestGoodDay(DailyForecast dfl, Event e){
        Date proposedDayDate = null;       
        Calendar proposedDayCal = null;
        
        /* Trasformo la Date di inizio dell'evento in Calendar */
        Date eventDayDate = e.getStartHour();
        Calendar eventDayCal = Calendar.getInstance();
        eventDayCal.setTime(eventDayDate);
        
        int size = dfl.getForecastCount();
        if(size > 4){
            Iterator it = dfl.getForecastList().iterator();
            for(int i=0; i<=3; i++) it.next(); 
            
            boolean newDayFound = false;
            while(it.hasNext() && !newDayFound){
                Forecast f = (Forecast) it.next();
                String weather = f.getWeatherInstance(0).getWeatherName();
                if(weather.equals("Clear") || weather.equals("Clouds")){
                    newDayFound = true;
                    /* Trasformo la Date proposta (f.getDateTime()) in Calendar */
                    proposedDayCal = Calendar.getInstance();
                    proposedDayCal.setTime(f.getDateTime());
                    /* Modifico i campi HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND
                       copiandoli dall'oggetto Calendar eventDayCal */
                    proposedDayCal.set(HOUR_OF_DAY, eventDayCal.get(HOUR_OF_DAY));
                    proposedDayCal.set(MINUTE, eventDayCal.get(MINUTE));
                    proposedDayCal.set(SECOND, eventDayCal.get(SECOND));
                    proposedDayCal.set(MILLISECOND, eventDayCal.get(MILLISECOND));
                    /* Trasformo il Calendar modificato in Date */
                    proposedDayDate = proposedDayCal.getTime();
                }
            }
            if(newDayFound) return proposedDayDate;
            else return null;
        }
        return null;
    }
    
    
}
    

